//
//  NotificationsWindowController.m
//  UIElementInspector
//
//  Copyright 2011 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#import "NotificationsWindowController.h"
#import "Preferences.h"
#import "UIElementUtilities.h"

void receiveNotification(AXObserverRef observer, AXUIElementRef element, CFStringRef notification, void *refcon);

void receiveNotification(AXObserverRef observer, AXUIElementRef element, CFStringRef notification, void *refcon)
{
    NotificationsWindowController *controller;
    controller = (NotificationsWindowController*) refcon;
    [controller logMessage:@"Received "];
    [controller logMessage:(NSString*)notification];
    [controller logMessage:@" from "];
    [controller logMessage:[controller desciptionForElement:element]];
    [controller logMessage:@"\n"];
}

@implementation NotificationsWindowController
@synthesize textView;

- (id)initWithWindow:(NSWindow *)window
{
    self = [super initWithWindow:window];
    if (self) {
        observerRef = NULL;
    }
    return self;
}

- (void)windowDidLoad
{
    [(NSPanel *)[self window] setBecomesKeyOnlyIfNeeded:YES];
}

- (NSString*)desciptionForElement:(AXUIElementRef)element
{
    NSString* title = [UIElementUtilities titleOfUIElement:element];
    NSString* role = [UIElementUtilities roleOfUIElement:element];
    NSString* msg;
    if (title)
        msg = [[NSString alloc]initWithFormat:@"%@ (%@)",role,title];
    else
        msg = [[NSString alloc] initWithFormat:@"%@",role];
    return msg;
}

- (void)logElement:(AXUIElementRef)element withIndent:(int)indent
{
    int i;
    for (i=0; i<indent; i++)
        [self logMessage:@"  "];
    [self logMessage:[self desciptionForElement:element]];
    [self logMessage:@"\n"];
}

- (BOOL)observeUIElement:(AXUIElementRef)element
{
    NSArray* notifications = [[Preferences sharedPreferences] enabledNotifications];
    NSString* notification;
    for (notification in notifications) {
        AXError err = AXObserverAddNotification(observerRef, element, (CFStringRef) notification, self);
        if (err != kAXErrorSuccess && err != kAXErrorNotificationUnsupported) {
            NSString *msg = [[NSString alloc] initWithFormat:@"Error adding notification %@: %d\n", notification, err];
            [self logMessage:msg];
            return NO;
        }
    }
    return YES;
}

- (void)stopObservingElement:(AXUIElementRef)element
{
    NSArray* notifications = [[Preferences sharedPreferences] enabledNotifications];
    NSString* notification;
    for (notification in notifications) {
        AXError err = AXObserverRemoveNotification(observerRef, element, (CFStringRef) notification);
        if (err != kAXErrorSuccess && err != kAXErrorNotificationUnsupported && err != kAXErrorNotificationNotRegistered) {
            NSString *msg = [[NSString alloc] initWithFormat:@"Error removing notification %@: %d\n", notification, err];
            [self logMessage:msg];
            return;
        }
    }
}

- (void)observeUIElementAndChildren:(AXUIElementRef)element
{
    if (![self observeUIElement:element])
        return;
    NSArray* children = (NSArray*) [UIElementUtilities valueOfAttribute:@"AXChildren" ofUIElement:element];
    if (children) {
        id child;
        for (child in children) {
            [self observeUIElementAndChildren:(AXUIElementRef)child];
        }
    }
}

- (void)observeApplicationOfUIElement:(AXUIElementRef)element
{
    
    AXUIElementRef thisElement = element;
    while (![UIElementUtilities isApplicationUIElement:thisElement]) {
        thisElement = [UIElementUtilities parentOfUIElement:thisElement];
    }
    [self observeUIElementAndChildren:thisElement];
    [self logMessage:@"Observing notifications for all UI Elements in "];
    [self logMessage:[UIElementUtilities titleOfUIElement:thisElement]];
    [self logMessage:@"\n"];
}

- (void)observeNotificationsForUIElement:(AXUIElementRef)element
{
    pid_t pid = [UIElementUtilities processIdentifierOfUIElement:element];
    if (pid)
    {
        [[self window] orderFront:NULL];
        AXError err = AXObserverCreate(pid, receiveNotification, &observerRef);
        if (err != kAXErrorSuccess) {
            NSString *msg = [[NSString alloc] initWithFormat:@"Error creating observer: %d\n", err];
            [self logMessage:msg];
            return;
        }
        if ([[Preferences sharedPreferences] notificationsForSingleElement])
            [self observeUIElement:element];
        else
            [self observeApplicationOfUIElement:element];
        CFRunLoopAddSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observerRef), kCFRunLoopDefaultMode);
    }
}

- (void)logMessage:(NSString*)message {
    NSRange endRange;
    
    endRange.location = [[textView textStorage] length];
    endRange.length = 0;
    [textView replaceCharactersInRange:endRange withString:message];
    endRange.length = [message length];
    [textView scrollRangeToVisible:endRange];
}

- (void)stopObserving {
    if (!observerRef)
        return;
    
    CFRelease(observerRef);
    observerRef = NULL;
    [self close];
}

@end